package com.yzy.fly.config;

import com.baomidou.mybatisplus.core.override.MybatisMapperProxy;
import com.google.common.collect.Lists;
import com.yzy.fly.annotation.Loader;
import com.yzy.fly.annotation.RoleChildren;
import com.yzy.fly.kernel.model.FlyBasicModel;
import com.yzy.fly.kernel.model.ability.role.RoleRedisTemplate;
import com.yzy.fly.kernel.controller.FlyBasicController;
import com.yzy.fly.kernel.mapper.FlyBasicMapper;
import com.yzy.fly.kernel.model.ability.role.Role;
import com.yzy.fly.kernel.service.FlyBasicService;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

@Component
public class FlySpringContextUtil extends SpringContextUtil {

    /**
     * find model‘s childrens mapper list
     * @param requiredModelType
     * @return
     */
    public static List<FlyBasicMapper> findFlyMappersModelChildrens(Class<?> requiredModelType) {
        List<FlyBasicMapper> mappers = Lists.newArrayListWithCapacity(0);
        List<Class> modelClasses = Lists.newArrayListWithCapacity(0);
        fillModelClassesAndMappers(requiredModelType,mappers,modelClasses);
        for (int i = 0; i < modelClasses.size(); i++) {
            Class modeType =  modelClasses.get(i);
            FlyBasicMapper flyBasicMapper = findFlyBasicMapper(modeType);
            if (flyBasicMapper != null) {
                mappers.add(flyBasicMapper);
            }
        }
        return mappers;
    }

    private static void fillModelClassesAndMappers(Class<?> requiredModelType, List<FlyBasicMapper> mappers, List<Class> modelClasses) {
        Field[] fields = requiredModelType.getDeclaredFields();
        if (ArrayUtils.isNotEmpty(fields)) {
            FlyBasicMapper flyBasicMapper = null;
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                boolean roleChildrenAnnotationPresent = field.isAnnotationPresent(RoleChildren.class);
                if (!roleChildrenAnnotationPresent) {
                    continue;
                }
                RoleChildren roleChildren = field.getAnnotation(RoleChildren.class);
                boolean annotationPresent = field.isAnnotationPresent(Loader.class);
                if (annotationPresent) {
                    Loader loaderAnn = field.getAnnotation(Loader.class);
                    Class<? extends FlyBasicMapper> cls = loaderAnn.mapperBean();
                    if (FlyBasicMapper.class != cls) {
                        flyBasicMapper = SpringContextUtil.getBean(cls);
                    }
                }
                if (flyBasicMapper == null) {
//                    Class<? extends FlyBasicMapper> cls = roleChildren.loaderBean();
//                    if (FlyBasicService.class != cls) {
//                        flyBasicMapper = SpringContextUtil.getBean(cls);
//                    }
                }
                Class<?> modelType = field.getType();
                if (modelType == List.class) {
                    ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
                    modelType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
                }
                if (flyBasicMapper == null) {
                    modelClasses.add(modelType);
//                    flyBasicMapper = findFlyBasicMapper(modelType);
                }
                if (null != flyBasicMapper) {
                    mappers.add(flyBasicMapper);
                }
                fillModelClassesAndMappers(modelType,mappers,modelClasses);
            }
        }
    }

    /**
     * @param requiredModelType
     * @return
     */
    public static FlyBasicController findFlyBasicController(Class<?> requiredModelType) {
        FlyBasicController flyBasicController = null;
        Map<String, FlyBasicController> beansOfType = getBeansOfType(FlyBasicController.class);
        for (Map.Entry<String, FlyBasicController> bean : beansOfType.entrySet()) {
            FlyBasicController value = bean.getValue();
            Class<?> parameterizedType = value.getEntityClassM();
            if (parameterizedType == requiredModelType) {
                flyBasicController = value;
                break;
            }
        }
        return flyBasicController;
    }

    /**
      * @param requiredModelType
      * @return
      */
    public static FlyBasicService findFlyBasicService(Class<?> requiredModelType) {
        FlyBasicService flyBasicService = null;
        Map<String, FlyBasicService> beansOfType = getBeansOfType(FlyBasicService.class);
        for (Map.Entry<String, FlyBasicService> bean : beansOfType.entrySet()) {
            FlyBasicService value = bean.getValue();
            Class<?> parameterizedType = value.getEntityClass();
            if (parameterizedType == requiredModelType) {
                flyBasicService = value;
                break;
            }
        }
        return flyBasicService;
    }

    public static <S extends FlyBasicService,K extends Serializable ,M extends FlyBasicModel> S findFlyService(Class<M> requiredModelType) {
        String beanName = requiredModelType.getSimpleName() + "Service";
        return getBean(beanName);
    }

    /**
     * @param requiredModelType
     * @return
     */
    public static FlyBasicMapper findFlyBasicMapper(Class<?> requiredModelType) {
        FlyBasicMapper flyBasicMapper = null;
        Map<String, FlyBasicMapper> beansOfType = getBeansOfType(FlyBasicMapper.class);
        for (Map.Entry<String, FlyBasicMapper> bean : beansOfType.entrySet()) {
            if (bean.getKey().equals("flyBasicMapper")) {
                continue;
            }
            FlyBasicMapper value = bean.getValue();
            MybatisMapperProxy mybatisMapperProxy = ((MybatisMapperProxy) Proxy.getInvocationHandler(value));
            try {
                Field mapperInterface = MybatisMapperProxy.class.getDeclaredField("mapperInterface");
                try {
                    mapperInterface.setAccessible(true);
                    Object o = mapperInterface.get(mybatisMapperProxy);
                    mapperInterface.setAccessible(false);
                    Type type = ((ParameterizedType) ((Class)o).getGenericInterfaces()[0]).getActualTypeArguments()[0];
                    if (type == requiredModelType) {
                        flyBasicMapper = value;
                        break;
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
        return flyBasicMapper;
    }

    public static <T extends Role> RoleRedisTemplate<String,T> findRoleRedisTemplate(Class<?> requiredModelType) {
        RoleRedisTemplate roleRedisTemplate = null;
        Map<String, RoleRedisTemplate> beansOfType = getBeansOfType(RoleRedisTemplate.class);
        for (Map.Entry<String, RoleRedisTemplate> bean : beansOfType.entrySet()) {
//            if (bean.getKey().equals("flyBasicMapper")) {
//                continue;
//            }
            RoleRedisTemplate value = bean.getValue();
            Type type = value.getRole();
            if (type == requiredModelType) {
                roleRedisTemplate = value;
                break;
            }
        }
        return roleRedisTemplate;
    }
}